﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using Microsoft.VisualBasic.FileIO;
using System.IO;

namespace com.hyrentals.common
{
    public class ZipObj
    {
        public int Zip { get; set; }
        public string City { get; set; }
        public string State { get; set; }
    }

    public class Zip
    {
        public string ZipCode { get; set; }
        public int CityId { get; set; }
        public int StateId { get; set; }
    }

    public static class RegionService
    {
        private const string ZIP_CODE_DATABASE_FILE_NAME = "zip_code_database.csv";
        private const string STATE_FILE_NAME = "state.csv";
        private const string CITY_FILE_NAME = "city.csv";
        private const string ZIP_FILE_NAME = "zip.csv";
        public static Dictionary<int, string> Id2City = new Dictionary<int, string>();
        public static Dictionary<int, string> Id2State = new Dictionary<int, string>();
        public static Dictionary<string, Zip> ZipDict = new Dictionary<string, Zip>();
        public static Dictionary<int, List<int>> State2City = new Dictionary<int, List<int>>();

        public static void Init()
        {
            string dataDir = ConfigurationManager.AppSettings["DataDir"];
            if (string.IsNullOrEmpty(dataDir))
            {
                dataDir = @"d:\data";
            }

            using (TextFieldParser parser = new TextFieldParser(Path.Combine(dataDir, STATE_FILE_NAME)))
            {
                parser.CommentTokens = new string[] { "#" };
                parser.SetDelimiters(new string[] { "," });
                parser.TrimWhiteSpace = true;
                parser.HasFieldsEnclosedInQuotes = true;

                parser.ReadLine();
                
                while (!parser.EndOfData)
                {
                    string[] fields = parser.ReadFields();
                    int stateId = int.Parse(fields[0]);
                    string stateStr = fields[1];
                    if (!Id2State.ContainsKey(stateId))
                    {
                        Id2State.Add(stateId, stateStr);
                    }
                }
            }

            using (TextFieldParser parser = new TextFieldParser(Path.Combine(dataDir, CITY_FILE_NAME)))
            {
                parser.CommentTokens = new string[] { "#" };
                parser.SetDelimiters(new string[] { "," });
                parser.TrimWhiteSpace = true;
                parser.HasFieldsEnclosedInQuotes = true;

                parser.ReadLine();

                while (!parser.EndOfData)
                {
                    string[] fields = parser.ReadFields();
                    int cityId = int.Parse(fields[0]);
                    string cityStr = fields[1];
                    if (!Id2City.ContainsKey(cityId))
                    {
                        Id2City.Add(cityId, cityStr);
                    }
                }
            }

            using (TextFieldParser parser = new TextFieldParser(Path.Combine(dataDir, ZIP_FILE_NAME)))
            {
                parser.CommentTokens = new string[] { "#" };
                parser.SetDelimiters(new string[] { "," });
                parser.TrimWhiteSpace = true;
                parser.HasFieldsEnclosedInQuotes = true;

                parser.ReadLine();

                while (!parser.EndOfData)
                {
                    string[] fields = parser.ReadFields();
                    string zipStr = fields[0];
                    int cityId = int.Parse(fields[1]);
                    int stateId = int.Parse(fields[2]);
                    if (!ZipDict.ContainsKey(zipStr))
                    {
                        ZipDict.Add(zipStr, new Zip { ZipCode = zipStr, CityId = cityId, StateId = stateId});
                    }
                }
            }
        }

        public static bool ValidateZipCode(string zipCode, out int cityId, out int stateId)
        {
            bool result = false;
            cityId = 0;
            stateId = 0;

            if (ZipDict.ContainsKey(zipCode))
            {
                result = true;
                Zip zip = ZipDict[zipCode];
                cityId = zip.CityId;
                stateId = zip.StateId;
            }

            return result;
        }

        public static void Generate()
        {
            string dataDir = ConfigurationManager.AppSettings["DataDir"];
            if (string.IsNullOrEmpty(dataDir))
            {
                dataDir = @"d:\data";
            }
            Dictionary<string, int> cityDic = new Dictionary<string, int>();
            Dictionary<string, int> stateDic = new Dictionary<string, int>();
            ZipObj[] zips = new ZipObj[42522];

            using (TextFieldParser parser = new TextFieldParser(Path.Combine(dataDir, ZIP_CODE_DATABASE_FILE_NAME)))
            {
                parser.CommentTokens = new string[] { "#" };
                parser.SetDelimiters (new string[] { "," });
                parser.TrimWhiteSpace = true;
                parser.HasFieldsEnclosedInQuotes = true;

                parser.ReadLine();
                int i = 0;
                while (!parser.EndOfData)
                {
                    string[] fields  = parser.ReadFields();
                    string zipStr = fields[0];
                    string cityStr = fields[2];
                    string stateStr = fields[5];
                    if(!cityDic.ContainsKey(cityStr))
                    {
                        cityDic.Add (cityStr, 0);
                    }
                    if(!stateDic.ContainsKey(stateStr))
                    {
                        stateDic.Add(stateStr, 0);
                    }
                    zips[i] = new ZipObj
                    {
                        Zip = int.Parse(zipStr),
                        City = cityStr,
                        State = stateStr
                    };
                    ++i;
                }
            }

            string[] cityArray = cityDic.Keys.ToArray();
            string[] stateArray = stateDic.Keys.ToArray();
            Array.Sort(cityArray);
            Array.Sort(stateArray);

            using (StreamWriter sw = new StreamWriter(Path.Combine(dataDir, "city.csv")))
            {
                sw.WriteLine("\"city_id\",\"city\"");
                for (int i = 0; i < cityArray.Length; ++i)
                {
                    sw.WriteLine(i+1 + "," + "\"" + cityArray[i] + "\"");
                }
            };

            using (StreamWriter sw = new StreamWriter(Path.Combine(dataDir, "state.csv")))
            {
                sw.WriteLine("\"state_id\",\"state\"");
                for (int i = 0; i < stateArray.Length; ++i)
                {
                    sw.WriteLine(i + 1 + "," + "\"" + stateArray[i] + "\"");
                }
            };

            for (int i = 0; i < cityArray.Length; ++i)
            {
                cityDic[cityArray[i]] = i + 1;
            }

            for (int i = 0; i < stateArray.Length; ++i)
            {
                stateDic[stateArray[i]] = i + 1;
            }

            using (StreamWriter sw = new StreamWriter(Path.Combine(dataDir, "zip.csv")))
            {
                sw.WriteLine("\"zip\",\"city_id\",\"state_id\"");
                for (int i = 0; i < zips.Length; ++i)
                {
                    int zip = zips[i].Zip;
                    int cityId = cityDic[zips[i].City];
                    int stateId = stateDic[zips[i].State];
                    sw.WriteLine("\"" + string.Format("{0:D5}", zip) + "\"" + "," + cityId + "," + stateId);

                    if (State2City.ContainsKey(stateId))
                    {
                        if (State2City[stateId] != null)
                            State2City[stateId].Add(cityId);
                        else
                            State2City[stateId] = new List<int>() { cityId };
                    }
                    else
                    {
                        State2City.Add(stateId, new List<int>() { cityId });
                    }
                }
            }
        }
    }
}
